package com.app.configuration.mybatis;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Properties;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.reflection.property.PropertyTokenizer;
import org.apache.ibatis.scripting.xmltags.ForEachSqlNode;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.app.util.database.mybatis.Page;


/**
 * 类名称：分页插件
 */
@Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class,Integer.class})})
@Component
public class PagePlugin implements Interceptor {

	@Value("${mybatis.page-dialect}")
	private String dialect;			//数据库方言
	
	@Value("${mybatis.page-pageSqlId}")
	private String pageSqlId;		//mapper.xml中需要拦截的ID(正则匹配)
	
	public Object intercept(Invocation invocation) throws Throwable {
		
		if (invocation.getTarget() instanceof StatementHandler) {  
            StatementHandler statementHandler = (StatementHandler) invocation.getTarget();  
            MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);  
            MappedStatement mappedStatement=(MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");
            //String selectId=mappedStatement.getId();
            
            if(mappedStatement.getId().matches(pageSqlId)){
            	
                BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");  
                // 分页参数作为参数对象parameterObject的一个属性  
                String sql = boundSql.getSql();
                
                Page page=(Page)(boundSql.getParameterObject());
                
                if(page.isUsePage()){	//启用分页
                
	                // 拦截到的prepare方法参数是一个Connection对象
	                Connection connection = (Connection) invocation.getArgs()[0];
	                setTotalRecord(page,page,mappedStatement,connection);
                }
                
                // 重写sql  
                String pageSql=generatePageSql(sql,page);
                //System.out.println("----分页后的sql--"+pageSql);
                metaStatementHandler.setValue("delegate.boundSql.sql", pageSql);            
              
                
            }
        } 
        
        return invocation.proceed();
		
		
		// TODO Auto-generated method stub
//		if(ivk.getTarget() instanceof RoutingStatementHandler){
//			RoutingStatementHandler statementHandler = (RoutingStatementHandler)ivk.getTarget();
//			BaseStatementHandler delegate = (BaseStatementHandler) ReflectHelper.getValueByFieldName(statementHandler, "delegate");
//			MappedStatement mappedStatement = (MappedStatement) ReflectHelper.getValueByFieldName(delegate, "mappedStatement");
			
//			if(mappedStatement.getId().matches(pageSqlId)){ //拦截需要分页的SQL
//				BoundSql boundSql = delegate.getBoundSql();
//				Object parameterObject = boundSql.getParameterObject();//分页SQL<select>中parameterType属性对应的实体参数，即Mapper接口中执行分页方法的参数,该参数不得为空
//				if(parameterObject==null){
//					throw new NullPointerException("parameterObject尚未实例化！");
//				}else{
					
					
//					Connection connection = (Connection) ivk.getArgs()[0];
//					String sql = boundSql.getSql();
//					//String countSql = "select count(0) from (" + sql+ ") as tmp_count"; //记录统计
//					String fhsql = sql;
//					String countSql = "select count(0) from (" + fhsql+ ")  tmp_count"; //记录统计 == oracle 加 as 报错(SQL command not properly ended)
//					PreparedStatement countStmt = connection.prepareStatement(countSql);
//					BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(),countSql,boundSql.getParameterMappings(),parameterObject);
//					setParameters(countStmt,mappedStatement,countBS,parameterObject);
//					ResultSet rs = countStmt.executeQuery();
//					int count = 0;
//					if (rs.next()) {
//						count = rs.getInt(1);
//					}
//					rs.close();
//					countStmt.close();
//					//System.out.println(count);
//					Page page = null;
//					if(parameterObject instanceof Page){	//参数就是Page实体
//						 page = (Page) parameterObject;
//						 page.setEntityOrField(true);	 
//						 page.setTotalResult(count);
//					}else{	//参数为某个实体，该实体拥有Page属性
//						Field pageField = ReflectHelper.getFieldByFieldName(parameterObject,"page");
//						if(pageField!=null){
//							page = (Page) ReflectHelper.getValueByFieldName(parameterObject,"page");
//							if(page==null)
//								page = new Page();
//							page.setEntityOrField(false); 
//							page.setTotalResult(count);
//							ReflectHelper.setValueByFieldName(parameterObject,"page", page); //通过反射，对实体对象设置分页对象
//						}else{
//							throw new NoSuchFieldException(parameterObject.getClass().getName()+"不存在 page 属性！");
//						}
//					}
//					String pageSql = generatePageSql(sql,page);
//					ReflectHelper.setValueByFieldName(boundSql, "sql", pageSql); //将分页sql语句反射回BoundSql.
//				}
//			}
//		}
//		return ivk.proceed();
	}

	
	/**
	 * 对SQL参数(?)设值,参考org.apache.ibatis.executor.parameter.DefaultParameterHandler
	 * @param ps
	 * @param mappedStatement
	 * @param boundSql
	 * @param parameterObject
	 * @throws SQLException
	 */
	@SuppressWarnings("unchecked")
	private void setParameters(PreparedStatement ps,MappedStatement mappedStatement,BoundSql boundSql,Object parameterObject) throws SQLException {
		ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
		List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
		if (parameterMappings != null) {
			Configuration configuration = mappedStatement.getConfiguration();
			TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
			MetaObject metaObject = parameterObject == null ? null: configuration.newMetaObject(parameterObject);
			for (int i = 0; i < parameterMappings.size(); i++) {
				ParameterMapping parameterMapping = parameterMappings.get(i);
				if (parameterMapping.getMode() != ParameterMode.OUT) {
					Object value;
					String propertyName = parameterMapping.getProperty();
					PropertyTokenizer prop = new PropertyTokenizer(propertyName);
					if (parameterObject == null) {
						value = null;
					} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
						value = parameterObject;
					} else if (boundSql.hasAdditionalParameter(propertyName)) {
						value = boundSql.getAdditionalParameter(propertyName);
					} else if (propertyName.startsWith(ForEachSqlNode.ITEM_PREFIX)&& boundSql.hasAdditionalParameter(prop.getName())) {
						value = boundSql.getAdditionalParameter(prop.getName());
						if (value != null) {
							value = configuration.newMetaObject(value).getValue(propertyName.substring(prop.getName().length()));
						}
					} else {
						value = metaObject == null ? null : metaObject.getValue(propertyName);
					}
					@SuppressWarnings("rawtypes")
					TypeHandler typeHandler = parameterMapping.getTypeHandler();
					if (typeHandler == null) {
						throw new ExecutorException("There was no TypeHandler found for parameter "+ propertyName + " of statement "+ mappedStatement.getId());
					}
					typeHandler.setParameter(ps, i + 1, value, parameterMapping.getJdbcType());
				}
			}
		}
	}
	
	/**
	 * 根据数据库方言，生成特定的分页sql
	 * @param sql
	 * @param page
	 * @return
	 */
	private String generatePageSql(String sql,Page page){
		
		if(page!=null && dialect!= null && !dialect.equals("")){
			
			if(page.isUsePage()){
			
				StringBuffer pageSql = new StringBuffer();
				if("oracle".equals(dialect)){
					pageSql.append("select * from (select tmp_tb.*,ROWNUM row_id from (");
					pageSql.append(sql);
					if(page.getSort()!=null&&!"".equals(page.getSort())){//排序
						pageSql.append(" order by "+page.getSort()+" "+(page.getOrder()==null?"":page.getOrder()));
					}
					pageSql.append(") tmp_tb where ROWNUM<=");
					pageSql.append(page.getCurrentResult()+page.getRows());
					pageSql.append(") where row_id>");
					pageSql.append(page.getCurrentResult());
				}else if("mysql".equals(dialect)){
					//pageSql.append("select * from (select tmp_tb.*,ROWNUM row_id from (");
					pageSql.append(sql);
					if(page.getSort()!=null&&!"".equals(page.getSort())){//排序
						pageSql.append(" order by "+page.getSort()+" "+(page.getOrder()==null?"":page.getOrder()));
					}
					
					pageSql.append(" limit "+ page.getCurrentResult()+","+page.getRows());
				}
				return pageSql.toString();
			}else{	//不启用分页
				if(page.getSort()!=null&&!"".equals(page.getSort())){//排序
					sql += (" order by "+page.getSort()+" "+(page.getOrder()==null?"":page.getOrder()));
				}
				return sql;
			}
		}else{
			return sql;
		}
	
	}
	
	public Object plugin(Object arg0) {
		// TODO Auto-generated method stub
		return Plugin.wrap(arg0, this);
	}

	public void setProperties(Properties p) {
		
	}
	
	/**
     * 给当前的参数对象page设置总记录数
     *
     * @param page
     *            Mapper映射语句对应的参数对象
     * @param mappedStatement
     *            Mapper映射语句
     * @param connection
     *            当前的数据库连接
     */
    private void setTotalRecord(Page page, Object parameterObject, MappedStatement mappedStatement,
            Connection connection) {
        BoundSql boundSql = mappedStatement.getBoundSql(page);
        String sql = boundSql.getSql();
        //sql = sql.replaceAll("\n|\r", " ");
        //int select_index = sql.toLowerCase().indexOf("select ");
        
        //int from_index = sql.toLowerCase().indexOf(" from ");
        //if(from_index == -1) {
        //	from_index = sql.toLowerCase().indexOf("from ");
       // }
        //System.out.println("----sql--"+sql);
        //System.out.println("selecteIndex:"+select_index +"####------####fromIndex:"+from_index)	;
        //System.out.println("--**********--"+sql.substring(from_index+5)+"---");
       // System.out.println("--111111--"+select_index+"----***"+from_index);
        //String countSql = "select count(*) from "+sql.substring(from_index+5);
        String countSql = "select count(0) from (" + sql+ ")  tmp_count";	//原代码
       // List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
       // BoundSql countBoundSql = new BoundSql(mappedStatement.getConfiguration(), countSql, parameterMappings,
        //        parameterObject);
        //ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject,
       //         countBoundSql);
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            pstmt = connection.prepareStatement(countSql);
            // 通过parameterHandler给PreparedStatement对象设置参数
            //parameterHandler.setParameters(pstmt);
            //setParameters(pstmt);
            setParameters(pstmt,mappedStatement,boundSql,parameterObject);
            // 之后就是执行获取总记录数的Sql语句和获取结果了。
            rs = pstmt.executeQuery();
            if (rs.next()) {
                int totalRecord = rs.getInt(1);
                // 给当前的参数page对象设置总记录数
                page.setTotalResult(totalRecord);
            }

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                if (rs != null)
                    rs.close();
                if (pstmt != null)
                    pstmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

}
